import java.util.Vector;
import java.io.*;
public class BlackJack {
	protected RandomCardDeck deck;
	protected static Chip playerChip;
	protected static Chip dealerChip;
	protected Chip pot;
	protected Vector dealerHand, playerHand;
	protected int dealerPoints, playerPoints;
	protected final static char HIT = 'H', STAND = 'S';
	
	public BlackJack() {
		Card card;
		dealerHand = new Vector();
		playerHand = new Vector();
		dealerChip = new Chip(0);
		playerChip = new Chip();
		pot = new Chip(0);
		deck = new RandomCardDeck();
		for (int c = 0; c < deck.getNumCards(); c++) {
			card = deck.getCard(c);
			if (card.isPictureCard()) {
				card.setValue(10);
			}
			else if (card.getFaceValue() == Card.ACE) {
				card.setValue(1);
			}
		}
	}
	
	public static void main(String[] args) {
		BufferedReader reader;
		char input = 0 ;
		BlackJack bj = new BlackJack();
		reader = new BufferedReader(new InputStreamReader(System.in));
		do {
			
				bj.play();
				do {
					if (playerChip.numChips > 0) {
						System.out.print("Continue Playing (Y/N)? ");
						try {
							input = Character.toUpperCase(reader.readLine().charAt(0));
						} catch (IOException ioe) { input = '?'; }
					}
					else {
						input = 'N';
						System.out.println("You lost " + dealerChip.numChips + " chips.");
					}
				} while (input != 'Y' && input != 'N');
		} while (input == 'Y' && playerChip.numChips > 0);
		//System.out.println("You left with " + playerChip.numChips);
	}
	
	public void play() {
		int input;
		dealerHand.clear();
		playerHand.clear();
		if (deck.getNumCardsLeft() <= 15) {
			System.out.println("Shuffling deck...");
			deck.shuffle();
		}
		bet();
		deal();
		output();
		if (playerBlackJack()) {
			System.out.println("Player has BlackJack!!!");
			((Card)dealerHand.elementAt(0)).setVisible(true);
			output();
			if (dealerBlackJack()) {
				playerChip.AddChips(pot.RemoveChips(0.5));
				dealerChip.AddChips(pot.AllIn());
				System.out.println("Dealer also has BlackJack.");
				System.out.println("Game is a PUSH.");
			}
			else {
				playerChip.AddChips(pot.AllIn() * 2);
				System.out.println("Player wins!");
			}
		}
		else if (dealerBlackJack()) {
			((Card)dealerHand.elementAt(0)).setVisible(true);
			System.out.println("Dealer has BlackJack.");
			output();
			dealerChip.AddChips(pot.AllIn());
			System.out.println("Player loses.");
		}
		else {
			do {
				try {
					input = getUserInput();
				}  catch (IOException ioe) { input = STAND; }
				if (input == HIT) {
					System.out.println("Dealt " + hitPlayer());
					output();
				}
				if (playerPoints > 21) {
					System.out.println("Player BUSTS!");
					((Card)dealerHand.elementAt(0)).setVisible(true);
					output();
					dealerChip.AddChips(pot.AllIn());
					System.out.println("Player loses.");
				}
			} while (input == HIT && playerPoints < 21);
			if (playerPoints <= 21) {
				System.out.println("Dealer's turn");
				((Card)dealerHand.elementAt(0)).setVisible(true);
				output();
				while (dealerPoints <= 16) {
					System.out.println("Dealer gets " + hitDealer());
					output();
				}
				if (dealerPoints > 21) {
					System.out.println("Dealer BUSTS!");
					playerChip.AddChips(pot.AllIn() * 2);
					System.out.println("Player wins!");
				}
				else if (dealerBlackJack()) {
					System.out.println("Dealer has BlackJack.");
					dealerChip.AddChips(pot.AllIn());
					System.out.println("Player loses.");
				}
				else if (dealerPoints == playerPoints) {
					playerChip.AddChips(pot.RemoveChips(0.5));
					dealerChip.AddChips(pot.AllIn());
					System.out.println("The game is a PUSH.");
				}
				else if (dealerPoints > playerPoints) {
					dealerChip.AddChips(pot.AllIn());
					System.out.println("Player loses.");
				}
				else {
					playerChip.AddChips(pot.AllIn() * 2);
					System.out.println("Player wins!");
				}
			}
		}
	}
	
	protected void bet() {
		pot.AddChips(playerChip.RemoveChips(10));
	}
	
	protected void deal() {
		Card card;
		playerHand.add(deck.deal());
		playerHand.add(deck.deal());
		card = deck.deal();
		card.setVisible(false);//sets the first dealer card to not visible
		dealerHand.add(card);
		dealerHand.add(deck.deal());
		updatePoints();
	}
	
	protected void output() {
		String d = "Dealer's Hand: ";
		String p = "Your Hand    : ";
		for (int c = 0; c < dealerHand.size(); c++) {
			d += (Card)dealerHand.elementAt(c) + " ";
		}
		for (int c = 0; c < playerHand.size(); c++) {
			p += (Card)playerHand.elementAt(c) + " ";
		}
		if (d.indexOf('?') == -1) {
			d += " (" + dealerPoints +")";
		}
		p += " (" + playerPoints +")";
		System.out.println(d);
		System.out.println(p);
		System.out.println("   Pot: " + pot.numChips);
		System.out.println("Dealer: " + dealerChip.numChips);
		System.out.println("Player: " + playerChip.numChips);
	}
	
	protected Card hitPlayer() {
		return hit(playerHand);
	}
	
	protected Card hitDealer() {
		return hit(dealerHand);
	}
	
	protected char getUserInput() throws IOException {
		BufferedReader reader;
		char input;
		reader = new BufferedReader(new InputStreamReader(System.in));
		do {
			System.out.print("(H)it or (S)tand? ");
			input = Character.toUpperCase(reader.readLine().charAt(0));
		} while (input != HIT && input != STAND);
		return input;
	}
	
	protected boolean dealerBlackJack() {
		if (dealerHand.size() == 2) {
			if (((Card)dealerHand.elementAt(1)).getFaceValue() == Card.ACE || ((Card)dealerHand.elementAt(0)).getVisible()) {
				return dealerPoints == 21;
			}
		}
		return false;
	}
	
	protected boolean playerBlackJack() {
		if (playerHand.size() == 2) {
			return playerPoints == 21;
		}
		return false;
	}
	
	private Card hit(Vector hand) {
		Card card = deck.deal();
		hand.add(card);
		updatePoints();
		return card;
	}
	
	protected void updatePoints() {
		playerPoints = addUpPoints(playerHand);
		dealerPoints = addUpPoints(dealerHand);
	}
	
	private int addUpPoints(Vector hand) {
		int points = 0;
		int nAces = 0;
		Card[] cards = new Card[hand.size()];
		hand.copyInto(cards);
		for (int c = 0; c < cards.length; c++) {
			points += cards[c].getValue();
			if (cards[c].getFaceValue() == Card.ACE) nAces++;
		}
		if (points <= 11 && nAces > 0) {
			points += 10;
		}
		return points;
	}
}
